package com.ib.algokit;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.atomic.AtomicReference;

import org.activequant.broker.IOrderTracker;
import org.activequant.calendar.IExchangeCalendar;
import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.account.Order;
import org.activequant.core.domainmodel.account.Portfolio;
import org.activequant.core.domainmodel.data.Candle;
import org.activequant.core.domainmodel.data.CandleSeries;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.data.QuoteSeries;
import org.activequant.core.domainmodel.data.TickerNews;
import org.activequant.core.domainmodel.data.TradeIndication;
import org.activequant.core.types.Currency;
import org.activequant.core.types.OrderSide;
import org.activequant.core.types.OrderType;
import org.activequant.core.types.TimeStamp;
import org.activequant.math.algorithms.EMAAccumulator;
import org.activequant.util.charting.TimeseriesCombinedChart;
import org.apache.log4j.Logger;

import com.ib.algokit.forex.CurrencyPairs;
import com.ib.algokit.ib.CommissionsCalculator;
import com.ib.algokit.ib.IBPortfolioManagingBroker;
import com.ib.algokit.ib.IBPortfolioManagingBroker.IBPosition;

public class IBMAnalyzerUtils implements AnalyzerPluginInterface{
	
	/**
	 * private final InstrumentAnalyzer instrumentAnalyzer;
	 */
	private final InstrumentAnalyzer instrumentAnalyzer;
	/**
	 * private final EMA ema;
	 */
	private final TraderProxy ema;
	
	private final EMAAccumulator ema10 = new EMAAccumulator();
	
	private final EMAAccumulator ema100 = new EMAAccumulator();
	
	
	public IBMAnalyzerUtils(InstrumentAnalyzer instrumentAnalyzer){
		this.instrumentAnalyzer=instrumentAnalyzer;
		this.ema=instrumentAnalyzer.getEMA();
	}
	boolean emaSetup=false;
	public void onCandle(Candle liveCandle) {
		// TODO Auto-generated method stub
		if(!emaSetup){
			//real time candles will be 5sec so I scale the period from 1d to 5sec when using these 1d historic candles
			int dayweight = (int) (InstrumentAnalyzer.ONE_DAY/(5*InstrumentAnalyzer.ONE_SECOND));
			ema10.setPeriod(10*dayweight);
			ema100.setPeriod(100*dayweight);
			CandleSeries historical1Dcandles=instrumentAnalyzer.getT_MINUS_1Y_HistoryCandles();
			for(int i=historical1Dcandles.size()-1;i>=0;i--){//lates candle is at index 0 so we go backwards
				Candle candle=historical1Dcandles.get(i);
				ema10.accumulate(candle.getOpenPrice());
				ema100.accumulate(candle.getOpenPrice());
			}
			emaSetup=true;
		}
		
		ema10.accumulate(liveCandle.getOpenPrice());
		ema100.accumulate(liveCandle.getOpenPrice());
		
		getRecommendationSignal();
	}

	
	public void getRecommendationSignal(){
		boolean sendHeartBeat=true;
		double ema10value = ema10.getMeanValue();
		double ema100value = ema100.getMeanValue();
		
		if(ema10value>ema100value){
			actUponRecommendation(Recommendation.BUY);
			//actUponRecommendation(Recommendation.SELL);
		}else{
			actUponRecommendation(Recommendation.DO_NOTHING);
		}
				
	}
	
	public void onTradeIndication(TradeIndication liveTradeIndication) {
		// TODO Auto-generated method stub
		
	}

	public void onQuote(Quote liveQuote) {
		// TODO Auto-generated method stub
		
	}

	public void onTickerNews(TickerNews liveTickerNews) {
		// TODO Auto-generated method stub
		
	}

	public TimeseriesCombinedChart getTimeseriesCombinedChart() {
		// TODO Auto-generated method stub
		return null;
	}

	public void updateExtremes(Candle liveCandle) {
		// TODO Auto-generated method stub
		
	}

	public long getNanoSecondsToWaitOnExecution() {
		// TODO Auto-generated method stub
		return 0;
	}

	public double getTargetProfit() {
		// TODO Auto-generated method stub
		return 0;
	}
	
	/**
	 * If I haven't already decided to buy and conditions are right, buy
	 */
	public void actUponRecommendation(Recommendation recommendation){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		InstrumentSpecification instrumentSpecification=instrumentAnalyzer.getInstrumentSpecification();

		//IPositionTracker positionTracker=instrumentAnalyzer.getPositionTracker();
		StockBracketPositionService.Tracker forexPositionTracker=(StockBracketPositionService.Tracker)instrumentAnalyzer.getPositionTracker();
		Logger log=instrumentAnalyzer.getLog();
		AtomicReference<QuoteSeries> liveQuoteSeriesBag = instrumentAnalyzer.getLiveQuoteSeriesBag();
		boolean waitForQuotes=true;
		if(waitForQuotes){
			if(liveQuoteSeriesBag==null||liveQuoteSeriesBag.get()==null||liveQuoteSeriesBag.get().size()==0){
				log.info("waiting for live Quote");
				return;
			}else{
				waitForQuotes=false;
			}
		}
		Quote latestQuote=liveQuoteSeriesBag.get().get(0);
		double bidPrice=latestQuote.getBidPrice();
		double askPrice=latestQuote.getAskPrice();

		String title=instrumentSpecification.getSymbol().toString()+"/"+instrumentSpecification.getCurrency();
		if(hasMainOrderReachedSubmitToAcceptTimeoutPeriod(forexPositionTracker)){
			StringBuffer msg=new StringBuffer(title+" main order reached timeout between submit and accept events. Something is probably wrong with the order. @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
			IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
			if(mainOrderTracker!=null){
				Order mainOrder = mainOrderTracker.getOrder();
				msg.append(mainOrder.toString());
			}
			log.warn(msg.toString());
		}else if(isMainBuyOrderAcceptedAndWorking(forexPositionTracker)){
			if(recommendation==Recommendation.BUY){
					log.info(title+" I already requested or submitted a BUY order so don't do anything (maybe modify in the future) @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
				
				return;//I already requested or submitted a BUY order so don't do anything (maybe modify in the future)
			}else if(recommendation==Recommendation.SELL){
				if(forexPositionTracker!=null){
					IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
					if(mainOrderTracker!=null){
						log.info(title+" got sell signal, cancelling main buy order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
						mainOrderTracker.cancel();						
						//if main order got partial fill, add that to sell order qty
						double mainOrderFilledQuantity = forexPositionTracker.getMainOrderFilledQuantity();						
						//todo submit new sell order here						
						Order order = new Order();
						order.setInstrumentSpecification(instrumentSpecification);
						order.setOrderTimeStamp(currentTimeStamp);
						order.setOrderSide(OrderSide.SELL);
						order.setLimitPrice(bidPrice);
						order.setOrderType(OrderType.LIMIT);
//						double optimalQuantityUSD = CommissionsCalculator.determineOrderQuantityForOptimalMinimumCommission(order);
//						double optimalQuantity = symbolCurrency==Currency.USD?optimalQuantityUSD:CurrencyPairs.convert(Currency.USD, optimalQuantityUSD, symbolCurrency);
//						if(mainOrderFilledQuantity>0){
//							optimalQuantity+=mainOrderFilledQuantity;	
//						}
						//double roundedOptimalQuantity=CurrencyPairs.roundUpAmount(symbolCurrency, optimalQuantity);
						order.setQuantity(100);
						StockBracketPositionService positionService = (StockBracketPositionService)instrumentAnalyzer.getPositionService();
						instrumentAnalyzer.setPositionTracker(mainOrderFilledQuantity>0?positionService.openPosition(order, mainOrderFilledQuantity):positionService.openPosition(order, 0));
//						if(isHistoryTrackingEnabled()){
//							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//							addHistory(new SignalDirectionChangeEvent(currentTimeStamp, "\\", instrumentSpecification, latestForexPairStates));							
//						}
						return;						
					}
				}
			}
		}else if(isMainSellOrderAcceptedAndWorking(forexPositionTracker)){
			if(recommendation==Recommendation.SELL){
					log.info(title+" I already requested or submitted a SELL order so don't do anything (maybe modify in the future) @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
				return;
			}else if(recommendation==Recommendation.BUY){
				if(forexPositionTracker!=null){
					IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
					if(mainOrderTracker!=null){
						log.info(title+" got buy signal, cancelling main sell order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
						mainOrderTracker.cancel();						
						//if main order got partial fill, add that to buy order qty
						double mainOrderFilledQuantity = forexPositionTracker.getMainOrderFilledQuantity();						
						//todo submit new sell order here						
						Order order = new Order();
						order.setInstrumentSpecification(instrumentSpecification);
						order.setOrderTimeStamp(currentTimeStamp);
						order.setOrderSide(OrderSide.BUY);
						order.setLimitPrice(askPrice);
						order.setOrderType(OrderType.LIMIT);
//						double optimalQuantityUSD = CommissionsCalculator.determineOrderQuantityForOptimalMinimumCommission(order);
//						double optimalQuantity = symbolCurrency==Currency.USD?optimalQuantityUSD:CurrencyPairs.convert(Currency.USD, optimalQuantityUSD, symbolCurrency);
//						if(mainOrderFilledQuantity>0){
//							optimalQuantity+=mainOrderFilledQuantity;	
//						}
//						double roundedOptimalQuantity=CurrencyPairs.roundUpAmount(symbolCurrency, optimalQuantity);
						order.setQuantity(100);
						StockBracketPositionService positionService = (StockBracketPositionService)instrumentAnalyzer.getPositionService();
						instrumentAnalyzer.setPositionTracker(mainOrderFilledQuantity>0?positionService.openPosition(order, mainOrderFilledQuantity):positionService.openPosition(order, 0));
//						if(isHistoryTrackingEnabled()){
//							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//							addHistory(new SignalDirectionChangeEvent(currentTimeStamp, "/", instrumentSpecification, latestForexPairStates));							
//						}
						return;						
					}
				}
			}
		}
		IExchangeCalendar exchangeCalendar = instrumentAnalyzer.getExchangeCalendar();
		Calendar calendar=GregorianCalendar.getInstance(exchangeCalendar.getTimeZone());
		calendar.setTimeInMillis(currentTimeStamp.getDate().getTime());
		int hourOfDay=calendar.get(Calendar.HOUR_OF_DAY);
		int minute=calendar.get(Calendar.MINUTE);
		boolean stopOpeningPositions=hourOfDay>=23; //optional
		boolean closeOpenPositions=hourOfDay>=23||(hourOfDay>=23&&minute>=50);//optional
		if(forexPositionTracker!=null && !forexPositionTracker.isClosed()){
			if(forexPositionTracker.hasMainOrderReachedMaximumInactivePeriod() && (isMainOrderAcceptedAndWorking(forexPositionTracker))){
				IOrderTracker mainOrderTracker=forexPositionTracker.getMainOrderTracker();
				if(mainOrderTracker!=null){
					log.info(title+" cancelling main order @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					mainOrderTracker.cancel();
//					if(isHistoryTrackingEnabled()){
//						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//						addHistory(new MainOrderInactiveTimeoutPeriodEvent(currentTimeStamp, "/", instrumentSpecification, latestForexPairStates));							
//					}
				}
				//price moved away from the order, cancel? position could be partially opened as well
				if(forexPositionTracker.getMainOrderFilledQuantity()!=0){
					//cancel main order
					//to do
					//partially executed
					log.info(title+" placing bracket orders @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					forexPositionTracker.placeBracketOrders();
					return;
				}else if(!forexPositionTracker.isClosed()){
					//nothing executed at all
					log.info(title+" close position @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					forexPositionTracker.close();
				}
			}else if(forexPositionTracker.hasLimitOrderReachedMaximumInactivePeriod()){
				IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
				if(limitOrderTracker!=null){				
					Order currentLimitOrder=limitOrderTracker.getOrder();
					if((currentLimitOrder.getOrderSide()==OrderSide.BUY && recommendation==Recommendation.SELL)||//If I am waiting to buy back and recommendation is sell, give it more time
							(currentLimitOrder.getOrderSide()==OrderSide.SELL && recommendation==Recommendation.BUY)){//If I am waiting to sell back and recommendation is buy, give it more time
						long currentLimitOrderMaximumInactivePeriod=forexPositionTracker.getLimitOrderMaximumInactivePeriod();
						long newLimitOrderMaximumInactivePeriod=2*currentLimitOrderMaximumInactivePeriod;
						forexPositionTracker.setLimitOrderMaximumInactivePeriod(newLimitOrderMaximumInactivePeriod);
						log.info(title+" lets give the limit order another 30 min @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
//						if(isHistoryTrackingEnabled()){
//							ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//							addHistory(new LimitOrderPeriodExtendedEvent(currentTimeStamp, "extended limit order period", instrumentSpecification, latestForexPairStates));							
//						}
						return;
					}
					log.info(title+" updating limit order because it reached max inactive period @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					updateLimitOrderForNoProfit(forexPositionTracker);					
					//limitOrderTracker.cancel(); update closer to current price but not get into a loss					
					if(forexPositionTracker.limitOrderUpdatedForNoProfit.getAndSet(true))//this will make sure it will only update it once
					{
						log.warn(title+" this shouldn't happen @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					}
//					if(isHistoryTrackingEnabled()){
//						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//						addHistory(new LimitOrderUpdatedForNoProfitEvent(currentTimeStamp, "updated limit order - inactive", instrumentSpecification, latestForexPairStates));							
//					}
//					}
				}
			}else if(forexPositionTracker.getLimitOrderTracker()!=null&& !forexPositionTracker.limitOrderUpdatedForNoProfit.get()&&
					((forexPositionTracker.getLimitOrderTracker().getOrder().getOrderSide()==OrderSide.BUY && recommendation==Recommendation.BUY)//If I am waiting to buy back and recommendation is buy, update my limit order to buy back without profit right away
							||
							(forexPositionTracker.getLimitOrderTracker().getOrder().getOrderSide()!=OrderSide.BUY && recommendation==Recommendation.SELL)//If I am waiting to buy back and recommendation is buy, update my limit order to buy back without profit right away
					)
			){
				IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
				if(limitOrderTracker!=null){
					log.info(title+" updating limit order because signal changed to opposite while waitng @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					updateLimitOrderForNoProfit(forexPositionTracker);
					//limitOrderTracker.cancel(); update closer to current price but not get into a loss					
					if(forexPositionTracker.limitOrderUpdatedForNoProfit.getAndSet(true))//this will make sure it will only update it once
					{
						log.warn(title+" this shouldn't happen @"+InstrumentAnalyzer.formatTimeStamp(currentTimeStamp));
					}
//					if(isHistoryTrackingEnabled()){
//						ForexPairStates latestForexPairStates=thePairStateHistory.get(thePairStateHistory.size()-1);
//						addHistory(new LimitOrderUpdatedForNoProfitEvent(currentTimeStamp, "updated limit order - opposite signal", instrumentSpecification, latestForexPairStates));							
//					}
				}
			}else if(forexPositionTracker.limitOrderUpdatedForNoProfit.get() && closeOpenPositions){//perhaps wait 30 min? or 
				forexPositionTracker.close();
			}
			if(!forexPositionTracker.isClosed()){
				return;
			}
		}		
		if(recommendation==Recommendation.DO_NOTHING){
			return;
		}		
		double MAX_LONG_QUANTITY_PER_POSITION=400000;
		double MAX_SHORT_QUANTITY_PER_POSITION=-400000;
		//get current position
		if(ema.getBroker() instanceof IBPortfolioManagingBroker){
			IBPortfolioManagingBroker broker =(IBPortfolioManagingBroker)ema.getBroker();
			Portfolio portfolio=broker.getBrokerAccount().getPortfolio();
			IBPosition position=portfolio.hasPosition(instrumentSpecification) ? (IBPosition)portfolio.getPosition(instrumentSpecification) : null;
			if(position!=null){
				double currentQuantity=position.getQuantity();
				if(recommendation==Recommendation.BUY&&currentQuantity>=MAX_LONG_QUANTITY_PER_POSITION){
					return;
				}else if(recommendation==Recommendation.SELL&&currentQuantity<=MAX_SHORT_QUANTITY_PER_POSITION){
					return;
				}
			}
		}
		//if(hasRequestedToGoLong.get())return;//we already decided to open position once, don't buy once again
		//if(position!=null)return;//we already have a position, don't go deeper?
		Order order = new Order();		
		if(stopOpeningPositions){//don't submit new DAY orders(default) after 16:00, existing ones can work till 16:30 and then they get canceled by the exchange
			return;
		}
		order.setInstrumentSpecification(instrumentSpecification);
		order.setOrderTimeStamp(currentTimeStamp);
		if(recommendation==Recommendation.BUY){
			order.setOrderSide(OrderSide.BUY);
			order.setLimitPrice(askPrice);
		}else if(recommendation==Recommendation.SELL){
			order.setOrderSide(OrderSide.SELL);
			order.setLimitPrice(bidPrice);
		}
		//order.setOrderType(OrderType.LIMIT);
		order.setOrderType(OrderType.MARKET);
//		
		order.setQuantity(100);
		//double approximateCommission = CommissionsCalculator.determineApproximateCommissionForOrder(buyOrder);
		//log.info("approximateCommission="+approximateCommission+" quantity="+optimalQuantity);	
		//IPositionService positionService = instrumentAnalyzer.getPositionService();
		StockBracketPositionService positionService = (StockBracketPositionService)instrumentAnalyzer.getPositionService();
		instrumentAnalyzer.setPositionTracker(positionService.openPosition(order,0));
	}
	
	/**
	 * Main Buy Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainBuyOrderAcceptedAndWorking(StockBracketPositionService.Tracker positionTracker){
		boolean isMainOrderWorking = isMainOrderAcceptedAndWorking(positionTracker);
		if(!isMainOrderWorking){
			return false;
		}		
		StockBracketPositionService.Tracker posTracker=(StockBracketPositionService.Tracker)positionTracker;
		Order order=null;
		if(posTracker!=null){
			order=posTracker.getMainOrderTracker().getOrder();			
		}
		return order!=null && order.getOrderSide()==OrderSide.BUY;
	}
	/**
	 * Main Sell Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainSellOrderAcceptedAndWorking(StockBracketPositionService.Tracker positionTracker){
		boolean isMainOrderWorking = isMainOrderAcceptedAndWorking(positionTracker);
		if(!isMainOrderWorking){
			return false;
		}		
		StockBracketPositionService.Tracker posTracker=(StockBracketPositionService.Tracker)positionTracker;
		Order order=null;
		if(posTracker!=null){
			order=posTracker.getMainOrderTracker().getOrder();			
		}
		return order!=null && (order.getOrderSide()==OrderSide.SELL || order.getOrderSide()==OrderSide.SHORT_SELL);
	}
	/**
	 * Main Order is still working if it has been accepted by IB, has not been canceled, and has not received the final commissions event
	 * @param positionTracker
	 * @return
	 */
	public boolean isMainOrderAcceptedAndWorking(StockBracketPositionService.Tracker positionTracker){
		TimeStamp mainOrderAcceptedTimeStamp=null;
		TimeStamp mainOrderCanceledTimeStamp=null;
		TimeStamp mainOrderCompletedAndCommissionsReceivedTimeStamp=null;
		if(positionTracker!=null){			
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
			mainOrderCanceledTimeStamp = positionTracker.getMainOrderCanceledTimeStamp();
			mainOrderCompletedAndCommissionsReceivedTimeStamp = positionTracker.getMainOrderCompletedAndCommissionsReceivedTimeStamp();

		}
		return positionTracker!=null && mainOrderAcceptedTimeStamp!=null && mainOrderCanceledTimeStamp==null && mainOrderCompletedAndCommissionsReceivedTimeStamp==null;
	}

	public boolean isMainOrderSubmittedAndNotAccepted(StockBracketPositionService.Tracker positionTracker){
		TimeStamp mainOrderSubmittedTimeStamp=null;
		TimeStamp mainOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			mainOrderSubmittedTimeStamp = positionTracker.getMainOrderSubmittedTimeStamp();
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && mainOrderSubmittedTimeStamp!=null && mainOrderAcceptedTimeStamp==null;
	}

	public boolean isLimitOrderSubmittedAndNotAccepted(StockBracketPositionService.Tracker positionTracker){
		TimeStamp limitOrderSubmittedTimeStamp=null;
		TimeStamp limitOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			limitOrderSubmittedTimeStamp = positionTracker.getLimitOrderSubmittedTimeStamp();
			limitOrderAcceptedTimeStamp = positionTracker.getLimitOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && limitOrderSubmittedTimeStamp!=null && limitOrderAcceptedTimeStamp==null;
	}

	public boolean isStopOrderSubmittedAndNotAccepted(StockBracketPositionService.Tracker positionTracker){
		TimeStamp stopOrderSubmittedTimeStamp=null;
		TimeStamp stopOrderAcceptedTimeStamp=null;
		if(positionTracker!=null){
			stopOrderSubmittedTimeStamp = positionTracker.getStopOrderSubmittedTimeStamp();
			stopOrderAcceptedTimeStamp = positionTracker.getStopOrderAcceptedTimeStamp();
		}
		return positionTracker!=null && stopOrderSubmittedTimeStamp!=null && stopOrderAcceptedTimeStamp==null;
	}

	public boolean hasMainOrderReachedSubmitToAcceptTimeoutPeriod(StockBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp mainOrderSubmittedTimeStamp=null;
		TimeStamp mainOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			mainOrderSubmittedTimeStamp = positionTracker.getMainOrderSubmittedTimeStamp();
			mainOrderAcceptedTimeStamp = positionTracker.getMainOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && mainOrderSubmittedTimeStamp!=null && mainOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(mainOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}

	public boolean hasLimitOrderReachedSubmitToAcceptTimeoutPeriod(StockBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp limitOrderSubmittedTimeStamp=null;
		TimeStamp limitOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			limitOrderSubmittedTimeStamp = positionTracker.getLimitOrderSubmittedTimeStamp();
			limitOrderAcceptedTimeStamp = positionTracker.getLimitOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && limitOrderSubmittedTimeStamp!=null && limitOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(limitOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}

	public boolean hasStopOrderReachedSubmitToAcceptTimeoutPeriod(StockBracketPositionService.Tracker positionTracker){
		TimeStamp currentTimeStamp=ema.getTimeStampService().currentTimeStamp();
		TimeStamp stopOrderSubmittedTimeStamp=null;
		TimeStamp stopOrderAcceptedTimeStamp=null;
		long orderSubmitToAcceptTimeoutPeriod=0;
		if(positionTracker!=null){
			stopOrderSubmittedTimeStamp = positionTracker.getStopOrderSubmittedTimeStamp();
			stopOrderAcceptedTimeStamp = positionTracker.getStopOrderAcceptedTimeStamp();
			orderSubmitToAcceptTimeoutPeriod = positionTracker.getOrderSubmitToAcceptTimeoutPeriod();
		}
		return positionTracker!=null && stopOrderSubmittedTimeStamp!=null && stopOrderAcceptedTimeStamp==null &&
		(currentTimeStamp.getNanoseconds()>(stopOrderSubmittedTimeStamp.getNanoseconds()+orderSubmitToAcceptTimeoutPeriod));
	}
	
	private void updateLimitOrderForNoProfit(StockBracketPositionService.Tracker forexPositionTracker){
		IOrderTracker limitOrderTracker=forexPositionTracker.getLimitOrderTracker();
		Order currentLimitOrder=limitOrderTracker.getOrder();
		double limitOrderAveragePrice=forexPositionTracker.getLimitOrderAveragePrice();
		double limitOrderFilledQuantity=forexPositionTracker.getLimitOrderFilledQuantity();
		double limitOrderCommission=forexPositionTracker.getLimitOrderCommission();
		Currency limitOrderCommissionCurrency=forexPositionTracker.getLimitOrderCommissionCurrency();
		double mainOrderAveragePrice=forexPositionTracker.getMainOrderAveragePrice();
		double mainOrderFilledQuantity=forexPositionTracker.getMainOrderFilledQuantity();
		double mainOrderCommission=forexPositionTracker.getMainOrderCommission();
		Currency mainOrderCommissionCurrency=forexPositionTracker.getMainOrderCommissionCurrency();
		double stopOrderAveragePrice=forexPositionTracker.getStopOrderAveragePrice();
		double stopOrderFilledQuantity=forexPositionTracker.getStopOrderFilledQuantity();
		double stopOrderCommission=forexPositionTracker.getStopOrderCommission();
		Currency stopOrderCommissionCurrency=forexPositionTracker.getStopOrderCommissionCurrency();
		InstrumentSpecification instrumentSpecification=instrumentAnalyzer.getInstrumentSpecification();
		String symbol = instrumentSpecification.getSymbol().toString();
		Currency quoteCurrency = instrumentSpecification.getCurrency();
		double mainOrderCommissionInQuoteCurrency=(quoteCurrency==mainOrderCommissionCurrency)? mainOrderCommission : CurrencyPairs.convert(mainOrderCommissionCurrency, mainOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		double stopOrderCommissionInQuoteCurrency=0;
		if(stopOrderCommissionCurrency!=null){
			stopOrderCommissionInQuoteCurrency=(quoteCurrency==stopOrderCommissionCurrency)? stopOrderCommission : CurrencyPairs.convert(stopOrderCommissionCurrency, stopOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		}
		double limitOrderCommissionInQuoteCurrency=0;
		if(limitOrderCommissionCurrency!=null){
			limitOrderCommissionInQuoteCurrency=(quoteCurrency==limitOrderCommissionCurrency)? limitOrderCommission : CurrencyPairs.convert(limitOrderCommissionCurrency, limitOrderCommission, quoteCurrency);//convert commissions from quote currency for my easier bookeeping
		}						
		double totalCommissionsInQuoteCurrency=mainOrderCommissionInQuoteCurrency+stopOrderCommissionInQuoteCurrency+limitOrderCommissionInQuoteCurrency;
		double outstandingBalance=(mainOrderAveragePrice*mainOrderFilledQuantity)+totalCommissionsInQuoteCurrency-(limitOrderAveragePrice*limitOrderFilledQuantity)-(stopOrderAveragePrice*stopOrderFilledQuantity);
		double outstandingQuantity=mainOrderFilledQuantity-stopOrderFilledQuantity-limitOrderFilledQuantity;
		double price=outstandingBalance/outstandingQuantity;
		Order updateLimitOrder=new Order(currentLimitOrder);
		updateLimitOrder.setId(null);
		if(updateLimitOrder.getOrderSide()==OrderSide.BUY){
			price=CurrencyPairs.roundCurrencyPrice(Currency.toClass(symbol), quoteCurrency, price, BigDecimal.ROUND_DOWN);
		}else{
			price=CurrencyPairs.roundCurrencyPrice(Currency.toClass(symbol),quoteCurrency, price, BigDecimal.ROUND_UP);
		}
		updateLimitOrder.setLimitPrice(price);
		updateLimitOrder.setQuantity(outstandingQuantity);
		if (forexPositionTracker.isSimulateOrderUpdatesEnabled()) {
			if (!forexPositionTracker.pendingChainedLimitOrderHolder.compareAndSet(null, updateLimitOrder)) {
				throw new IllegalStateException("another limit update is pending: can not proceed");
			}
			limitOrderTracker.cancel();
		} else {
			limitOrderTracker.update(updateLimitOrder);
		}

	}

}
